from django import template
from django.db import models

from lib.fields import PickledManyToManyField
from lib.helpers import reverse
from lib.template import tag_params
from history.helpers import iter_original_fields

register = template.Library()

class TemplateForField(template.Node):
    def __init__(self, field, value1, value2):
        self.field = template.Variable(field)
        self.value1 = template.Variable(value1)
        self.value2 = template.Variable(value2)

    def render(self, context):
        try:
            field = self.field.resolve(context)
            value1 = self.value1.resolve(context)
            value2 = self.value2.resolve(context)
            # one of them can be None
            if isinstance(field, models.ForeignKey):# or isinstance(value2, models.Model):
                class_name = '_model'
            else:
                class_name = value1.__class__.__name__.lower()
            template_name_list = ['templatetags/compare/%s.html' % class_name,
                                  'templatetags/compare/_default.html']
            return template.loader.select_template(template_name_list).render(context)
        except template.VariableDoesNotExist:
            return ''

@register.tag
def compare_fields(parser, token):
    field, value1, value2 = tag_params(token, r'(\w+) (\w+) (\w+)')
    return TemplateForField(field, value1, value2)

@register.inclusion_tag('templatetags/compare/_main.html')
def compare_objects(object1, object2, show_similar=False):
    if object1.__class__ != object2.__class__:
        raise ValueError('Objects must be instances of the same class.')
    changed_fields = []
    for field in iter_original_fields(object1):
        # handling `choices`
        get_field_display = 'get_%s_display' % field.name
        if hasattr(object1, get_field_display):
            value1 = getattr(object1, get_field_display)()
            value2 = getattr(object2, get_field_display)()
        else:
            value1 = field.value_from_object(object1)
            value2 = field.value_from_object(object2)
        if isinstance(field, PickledManyToManyField):
            # then values are querysets, they can't be compared directly
            value1, value2 = [v for v in value1], [v for v in value2]
        elif isinstance(field, models.ForeignKey):
            value1 = getattr(object1, field.name)
            value2 = getattr(object2, field.name)
        if show_similar or value1 != value2:
            changed_fields.append((field, value1, value2))
    return {'fields': changed_fields}
